/*
 * Copyright (c) 2012
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.dcarew.eclipseicons;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * Scan for and extract icons from Eclipse plugins.
 * 
 * @author devoncarew
 */
public class EclipseIcons {
  private static int pluginCount;
  private static int iconCount;

  private static String version;

  /**
   * @param args
   */
  public static void main(String[] args) {
    if (args.length != 2) {
      System.out.println("usage: EclipseIcons <src_dir> <dest_dir>");
      System.out.println("       where <src_dir> is a directory containing Eclipse plugins");
      return;
    }

    File srcDir = new File(args[0]);
    File destDir = new File(args[1]);

    if (!srcDir.exists() || !srcDir.canRead()) {
      System.err.println("Cannot read src dir: " + srcDir.getPath());
      return;
    }

    if (!destDir.exists()) {
      destDir.mkdirs();
    }

    if (!destDir.canWrite()) {
      System.err.println("Cannot write to dest dir: " + destDir.getPath());
      return;
    }

    System.out.println("Processing " + srcDir.getPath() + "...");

    for (File child : srcDir.listFiles()) {
      if (child.isDirectory()) {
        handleDirectory(child, destDir);
      } else if (child.getName().endsWith(".jar")) {
        handleJar(child, destDir);
      }
    }

    System.out.println("Copied " + iconCount + " icons from " + pluginCount + " plugins.");

    try {
      File indexFile = createIndex(destDir);

      System.out.println("generated icon overview: " + indexFile.getPath());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private static void close(InputStream in) {
    try {
      in.close();
    } catch (IOException ioe) {
      // ignore
    }
  }

  private static void close(OutputStream out) {
    try {
      out.close();
    } catch (IOException ioe) {
      // ignore
    }
  }

  private static void collectIconInfo(File destDir, File dir, List<IconInfo> icons) {
    for (File child : dir.listFiles()) {
      if (child.isDirectory()) {
        collectIconInfo(destDir, child, icons);
      } else if (isImageName(child.getName())) {
        icons.add(new IconInfo(destDir, child));
      }
    }
  }

  private static void copy(InputStream in, OutputStream out) throws IOException {
    final byte[] buffer = new byte[10240];

    int count = in.read(buffer);

    while (count != -1) {
      out.write(buffer, 0, count);
      count = in.read(buffer);
    }
  }

  private static void copyFile(File file, File destDir) {
    File destFile = new File(destDir, file.getName());

    if (!destDir.exists()) {
      destDir.mkdirs();
    }

    InputStream in = null;
    OutputStream out = null;

    try {
      in = new FileInputStream(file);
      out = new FileOutputStream(destFile);

      copy(in, out);
    } catch (IOException ioe) {
      // ignore
    } finally {
      if (in != null) {
        close(in);
      }
      if (out != null) {
        close(out);
      }
    }

    deleteIfNonStandard(destFile);
  }

  private static void copyFile(String name, InputStream in, File destDir) {
    iconCount++;

    File destFile = new File(destDir, name);

    File parentDir = destFile.getParentFile();

    if (!parentDir.exists()) {
      parentDir.mkdirs();
    }

    OutputStream out = null;

    try {
      out = new FileOutputStream(destFile);

      copy(in, out);
    } catch (IOException ioe) {
      // ignore
    } finally {
      if (out != null) {
        close(out);
      }
    }

    deleteIfNonStandard(destFile);
  }

  private static void copyImages(File srcDir, File destDir) {
    for (File child : srcDir.listFiles()) {
      if (child.isDirectory()) {
        copyImages(child, new File(destDir, child.getName()));
      } else if (isImageName(child.getName())) {
        copyFile(child, destDir);
      }
    }
  }

  private static File createIndex(File dir) throws IOException {
    // find all the images
    List<IconInfo> icons = new ArrayList<IconInfo>();

    collectIconInfo(dir, dir, icons);

    // sort and organize them
    Collections.sort(icons);

    // write out index.html
    return createIndexFile(dir, icons);
  }

  private static File createIndexFile(File dir, List<IconInfo> icons) throws IOException {
    File indexFile = new File(dir, "index.html");

    PrintWriter writer = new PrintWriter(indexFile);

    writer.println("<html>");
    writer.println("<style type=\"text/css\">");
    writer.println(".rounded { border-radius: 6px; background: #DDD; padding: 0px 0px 0px 6px; margin: 16px 0px 2px 0px; }");
    writer.println(".title { font-weight: bold; }");
    writer.println(".subtitle { font-weight: normal; padding: 0px 6px 0px 0px; }");
    writer.println(".content { margin: 0px 0px 0px 20px; }");
    writer.println(".footer { border-radius: 6px; background: #DDD; padding: 2px 2px 2px 6px; }");
    writer.println("</style>");
    writer.println("<body>");

    writer.println("<h3>Standard Eclipse icons</h3>");
    writer.println("These icons were pulled from ");

    if (version == null) {
      writer.print("an Eclipse distribution");
    } else {
      writer.print("Eclipse " + version);
    }

    writer.println(", and are covered under the <a href=\"http://www.eclipse.org/legal/epl-v10.html\">EPL</a>.");

    writeIcons("16x16 Icons", writer, get16x16Icons(icons), 4);
    writeIcons("Overlay Icons", writer, getOverlayIcons(icons), 4);
    writeIcons("Wizard Images", writer, getWizardIcons(icons), 3);

    writer.println("<br/><div class=\"footer\">Generated "
        + DateFormat.getDateInstance().format(new Date()) + "</div>");

    writer.println("</body></html");

    writer.close();

    return indexFile;
  }

  private static void deleteIfNonStandard(File file) {
    if (file.exists() && file.canWrite()) {
      IconInfo icon = new IconInfo(null, file);

      if (!icon.isStandardIcon()) {
        file.delete();

        iconCount--;
      }
    }
  }

  private static boolean filterImagePath(String name) {
    int length = name.length();

    int index = name.lastIndexOf('/');

    if (index != -1) {
      length -= index;
    }

    return length > 30;
  }

  private static List<IconInfo> get16x16Icons(List<IconInfo> icons) {
    List<IconInfo> results = new ArrayList<IconInfo>();

    for (IconInfo icon : icons) {
      if (icon.is16x16Icon()) {
        results.add(icon);
      }
    }

    return results;
  }

  private static List<IconInfo> getOverlayIcons(List<IconInfo> icons) {
    List<IconInfo> results = new ArrayList<IconInfo>();

    for (IconInfo icon : icons) {
      if (icon.isOverlayIcon()) {
        results.add(icon);
      }
    }

    return results;
  }

  private static List<IconInfo> getWizardIcons(List<IconInfo> icons) {
    List<IconInfo> results = new ArrayList<IconInfo>();

    for (IconInfo icon : icons) {
      if (icon.isWizardIcon()) {
        results.add(icon);
      }
    }

    return results;
  }

  private static void handleDirectory(File directory, File destDir) {
    if (!shouldHandlePlugin(directory.getName())) {
      return;
    }

    pluginCount++;

    File iconDir = new File(directory, "icons");

    if (iconDir.exists() && iconDir.canRead()) {
      copyImages(iconDir, destDir);
    }
  }

  private static void handleJar(File jarFile, File destDir) {
    if (!shouldHandlePlugin(jarFile.getName())) {
      return;
    }

    final String RUNTIME_PREFIX = "org.eclipse.core.runtime_";

    if (jarFile.getName().startsWith(RUNTIME_PREFIX)) {
      String v = jarFile.getName().substring(RUNTIME_PREFIX.length());

      // 3.7.0.v20110110.jar

      version = v.substring(0, 5);
    }

    pluginCount++;

    try {
      ZipFile zipFile = new ZipFile(jarFile);

      Enumeration<? extends ZipEntry> enumeration = zipFile.entries();

      while (enumeration.hasMoreElements()) {
        ZipEntry entry = enumeration.nextElement();

        if (entry.isDirectory()) {
          continue;
        }

        String name = entry.getName();

        if (!name.startsWith("icons/")) {
          continue;
        }

        name = name.substring("icons/".length());

        if (isImageName(name) && !filterImagePath(name)) {
          copyFile(name, zipFile.getInputStream(entry), destDir);
        }
      }
    } catch (IOException ioe) {
      // ignore - unable to read jarFile

    }
  }

  private static boolean isImageName(String name) {
    String[] suffixes = new String[] {".gif", ".png", ".jpg"};

    if (name.endsWith("SecurityPermission.gif")) {
      System.out.println("boo");
    }

    for (String suffix : suffixes) {
      if (name.endsWith(suffix)) {
        return true;
      }
    }

    return false;
  }

  private static boolean shouldHandlePlugin(String name) {
    if (name.startsWith("org.eclipse.jst")) {
      return false;
    }

    String[] prefixes = new String[] {"org.eclipse."};

    for (String prefix : prefixes) {
      if (name.startsWith(prefix)) {
        return true;
      }
    }

    return false;
  }

  private static void writeIcons(String title, PrintWriter writer, List<IconInfo> icons,
      int colCount) {
    writer.println("<div class=\"rounded\"><table width=\"100%\"><tr><td class=\"title\">" + title);
    writer.println("</td><td align=\"right\" class=\"subtitle\">"
        + NumberFormat.getInstance().format(icons.size()) + " icons");
    writer.println("</td></tr></table></div>");

    writer.println("<table class=\"content\">");
    writer.println("<tr>");

    int count = 0;

    for (IconInfo icon : icons) {
      if (0 == (count % colCount)) {
        writer.println("</tr>");
        writer.println("<tr>");
      }

      writer.print("  <td><a href=\"" + icon.getPath() + "\">");
      writer.print("<img src=\"" + icon.getPath() + "\" width=\"" + icon.getWidth()
          + "\" height=\"" + icon.getHeight() + "\">");
      writer.println("</a></td><td>" + icon.getName() + "</td>");

      count++;
    }

    writer.println("</tr>");
    writer.println("</table>");
  }

}
